In response to the ever-increasing complexity of computers, systems, and networks, computer hardware and software are typically designed to be modular, with a well-defined division of labor between software and/or hardware components. A well-known example is a device driver, which is a computer program or process that acts as an intermediary between higher-level programs, such as applications, and lower-level hardware, such as disk drives, input/output (I/O) channels, communication devices, and the like. Device drivers provide a well-defined interface between a device and the applications or operating systems that use the device. There may be many different device drivers on a system, and there may be duplicated instances of a particular kind of device driver. For example, a system may have one kind of device driver to handle keyboard input, another kind of device driver to handle access to system memory, and yet another kind of device driver or drivers to manage I/O with a hard disk or disk array.
Device drivers are but one example of processes that perform functions within a computer system. Each process is in essence a computer program and usually requires some computer memory, for example, for storing temporary data, caching data streams sent to or received from peripherals, such as serial ports or storage devices, and so on. Typically, processes must request some portion of system memory to be allocated to them for their use. However, system memory is a finite, limited resource which generally must be shared. In a system with multiple processes, a process that requests a certain amount of system memory may be allocated the amount of memory requested, an amount less than the amount of memory requested, or even no memory at all.
One problem faced by processes is the question of how much memory the process should request in excess of the minimum amount of memory required for the process to function. For example, a process such as a hard-disk device driver may improve overall system performance if it can keep a number of often-used disk sectors in local random access memory (RAM), because accessing RAM may be orders of magnitude faster than accessing data stored on a hard drive. The more disk sectors that can be cached to RAM, the better the system performance will be due to the decreased data access time. However, because the limited amount of RAM must also be shared among other processes, a hard-disk device driver should not be allocated all available RAM, else other processes cannot operate.
Therefore, system designers face a dilemma: if a process requests and is allocated the maximum amount of the memory that it may ever need, but the process never actually uses all of the memory that it has reserved for itself, that unused memory is wasted, since the memory cannot be used by any other process unless the memory is released by the process to which it has been reserved. Furthermore, if many processes request and are allocated memory that they never use, the total system performance may be impaired, perhaps fatally. On the other hand, if a process requests and is allocated only the minimum amount of memory that it requires at the moment, but later finds that it needs more, its later request may be denied, if all of the remaining system memory has been taken by other processes. When this happens, the process may fail or operate at a reduced level of function or performance. For this reason, a process will typically request an amount of memory sufficient for all situations that the process may reasonably be expected to handle. As a result, memory tends to be wasted, and the cumulative amount of the memory wasted by all processes can be significant.
Adding to the problem is the fact that many processes, especially device drivers dealing with data or communications streams, may require a relatively large amount of memory only for temporary periods, and otherwise require a relatively small amount of memory. In conventional memory management systems that make no distinction between memory that a process requires for its operation and excess memory allocated to a process to enhance its performance, such processes may request the relatively large amount of memory despite the fact that large portions of the allocated memory may be unused for significant amounts of time.
One commonly used method to manage limited memory resources is the use of a “garbage collector”, which reclaims memory that will never be accessed or used again by a process. However, even in systems that implement garbage collection, memory may be reclaimed only if the process has finished using it. The term “garbage” collection reflects the fact that the memory reclaimed has been implicitly or explicitly discarded by the process.
Another attempted solution to the problems described above involves placing extra intelligence into the processes themselves, such as adding the ability for a process to monitor not only the memory that has been allocated to it, but also the memory that has not been allocated to any process, or unallocated memory, as well. Such a process may then request additional allocations when memory becomes available or relinquish memory when unallocated memory becomes scarce.
However, there are some inherent disadvantages to this approach. First, each process must include the ability to monitor unallocated memory, adding to the complexity and size each process and requiring an relatively expensive and difficult modification to the system software as a whole. Second, unless all processes have this additional ability, an unmodified (legacy) process may dominate the memory use or be dominated by the other processes. Third, even if all processes are upgraded, the competitive nature of this approach means that unless these processes cooperate, this may result in the very problem that the additional complexity was intended to solve—a lack of sufficient memory to satisfy stationary memory requirements due to one or more processes selfishly grabbing all available memory. In short, with multiple processes vying to use unallocated memory, a regulatory mechanism is required to balance the demand and claims of the various processes.
Accordingly, in light of the disadvantages described above, there exists a need to dynamically allocate discretionary memory among multiple processes.